 /*
  * $Header: /cvshome/build/org.osgi.service.condpermadmin/src/org/osgi/service/condpermadmin/BundleLocationCondition.java,v 1.18 2006/06/16 16:31:37 hargrave Exp $
  *
  * Copyright (c) OSGi Alliance (2005, 2006). All Rights Reserved.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  * http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */

 package org.osgi.service.condpermadmin;

 import java.security.AccessController ;
 import java.security.PrivilegedAction ;
 import java.util.Hashtable ;

 import org.osgi.framework.*;

 /**
  * Condition to test if the location of a bundle matches a pattern. Pattern
  * matching is done according to the filter string matching rules.
  *
  * @version $Revision: 1.18 $
  */
 public class BundleLocationCondition {
     private static final String CONDITION_TYPE = "org.osgi.service.condpermadmin.BundleLocationCondition";

     /**
      * Constructs a condition that tries to match the passed Bundle's location
      * to the location pattern.
      *
      * @param bundle The Bundle being evaluated.
      * @param info The ConditionInfo to construct the condition for. The args of
      * the ConditionInfo must be a single String which specifies the
      * location pattern to match against the Bundle location. Matching is
      * done according to the filter string matching rules. Any '*'
      * characters in the location argument are used as wildcards when
      * matching bundle locations unless they are escaped with a '\'
      * character.
      * @return Condition object for the requested condition.
      */
     static public Condition getCondition(final Bundle bundle, ConditionInfo info) {
         if (!CONDITION_TYPE.equals(info.getType()))
             throw new IllegalArgumentException (
                     "ConditionInfo must be of type \"" + CONDITION_TYPE + "\"");
         String [] args = info.getArgs();
         if (args.length != 1)
             throw new IllegalArgumentException ("Illegal number of args: "
                     + args.length);
         String bundleLocation = (String ) AccessController
                 .doPrivileged(new PrivilegedAction () {
                     public Object run() {
                         return bundle.getLocation();
                     }
                 });
         Filter filter = null;
         try {
             filter = FrameworkUtil.createFilter("(location="
                     + escapeLocation(args[0]) + ")");
         }
         catch (InvalidSyntaxException e) {
             // this should never happen, but just incase
 throw new RuntimeException ("Invalid filter: " + e.getFilter());
         }
         Hashtable matchProps = new Hashtable (2);
         matchProps.put("location", bundleLocation);
         return filter.match(matchProps) ? Condition.TRUE : Condition.FALSE;
     }

     private BundleLocationCondition() {
         // private constructor to prevent objects of this type
 }

     /**
      * Escape the value string such that '(', ')' and '\' are escaped. The '\'
      * char is only escaped if it is not followed by a '*'.
      *
      * @param value unescaped value string.
      * @return escaped value string.
      */
     private static String escapeLocation(String value) {
         boolean escaped = false;
         int inlen = value.length();
         int outlen = inlen << 1; /* inlen * 2 */

         char[] output = new char[outlen];
         value.getChars(0, inlen, output, inlen);

         int cursor = 0;
         for (int i = inlen; i < outlen; i++) {
             char c = output[i];
             switch (c) {
                 case '\\' :
                     if (i + 1 < outlen && output[i + 1] == '*')
                         break;
                 case '(' :
                 case ')' :
                     output[cursor] = '\\';
                     cursor++;
                     escaped = true;
                     break;
             }

             output[cursor] = c;
             cursor++;
         }

         return escaped ? new String (output, 0, cursor) : value;
     }
 }

